#include "../../../hw_ints.h"
#include "../../../hw_memmap.h"
#include "../../../hw_types.h"
#include "../../../src/timer.h"
#include "../../../src/interrupt.h"
#include "../osram96x16x1.h"
#include "worker.h"

#include <stdio.h>
//****************************************************************************
//
//	Worker
//
//****************************************************************************

extern unsigned long g_ulADCData;
extern unsigned long g_ulFlags;
extern unsigned long g_ulScore;


static unsigned char g_pucTetrisMatrix[192] = {0};

static unsigned char g_pucFigures[19][8] = { 	
												{ 0x0f,0x0f,0x0f,0x0f,0x00,0x00,0x00,0x00 }, // "O" -  0 
												{ 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00 }, // "I"	-  1
												{ 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03 }, // "I"	-  2
												{ 0x0c,0x0c,0x0f,0x0f,0x03,0x03,0x00,0x00 }, // "Z"	-  3
												{ 0x0f,0x0f,0x3c,0x3c,0x00,0x00,0x00,0x00 }, // "Z"	-  4
												{ 0x03,0x03,0x0f,0x0f,0x0c,0x0c,0x00,0x00 }, // "S"	-  5
												{ 0x3c,0x3c,0x0f,0x0f,0x00,0x00,0x00,0x00 }, // "S"	-  6
												{ 0x3f,0x3f,0x30,0x30,0x00,0x00,0x00,0x00 }, // "L"	-  7
												{ 0x0f,0x0f,0x03,0x03,0x03,0x03,0x00,0x00 }, // "L"	-  8
												{ 0x03,0x03,0x3f,0x3f,0x00,0x00,0x00,0x00 }, // "L"	-  9
												{ 0x0c,0x0c,0x0c,0x0c,0x0f,0x0f,0x00,0x00 }, // "L"	- 10
												{ 0x30,0x30,0x3f,0x3f,0x00,0x00,0x00,0x00 }, // "J"	- 11
												{ 0x0f,0x0f,0x0c,0x0c,0x0c,0x0c,0x00,0x00 }, // "J"	- 12
												{ 0x3f,0x3f,0x03,0x03,0x00,0x00,0x00,0x00 }, // "J"	- 13
												{ 0x03,0x03,0x03,0x03,0x0f,0x0f,0x00,0x00 }, // "J"	- 14
												{ 0x3f,0x3f,0x0c,0x0c,0x00,0x00,0x00,0x00 }, // "T"	- 15
												{ 0x03,0x03,0x0f,0x0f,0x03,0x03,0x00,0x00 }, // "T"	- 16
												{ 0x0c,0x0c,0x3f,0x3f,0x00,0x00,0x00,0x00 }, // "T"	- 17
												{ 0x0c,0x0c,0x0f,0x0f,0x0c,0x0c,0x00,0x00 }	 // "T"	- 18
											};

// tozi masiv 6te se pozlva za vujdane na sledva6tata rotaciq na teku6tiq element ( ne se setih za po-umen i udoben)
static const unsigned char g_pucRot[19] = { 0, 2, 1, 4, 3, 6, 5, 8, 9, 10, 7, 12, 13, 14, 11,16,17,18,15 };
// masiv za ma6tabirane na stoinostta na ADC v zavisimost ot goleminata na figurkata
static const unsigned char g_pucScale[19] = { 7, 5, 8, 7, 6, 7, 6, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7 };  

static unsigned char g_pucDisplayBuf[20] = { 0 };
static unsigned char g_pucLastNoCollision[16] = { 0 };

static char g_pcScoreStr[11] = { 0 };

static unsigned	char ucPrevShape = 0, ucCurrShape = 0,
					 ucPrevPos = 0, ucCurrPos = 0; 

static unsigned long  ulRowOffset = 0;
//**********************************************************************************************
//
// Function for clearing the Tetris Matrix
//
//**********************************************************************************************
void ClearTetrisMatrix(void)
{
	unsigned char ucIdx;
	for( ucIdx = 0; ucIdx < 192	; ucIdx+=4 )
	{
		*(unsigned long *)(g_pucTetrisMatrix + ucIdx) = 0;	
	}
}
//**********************************************************************************************
//
//  Function that update the current state of the Tetris Matrix ( g_pucTetrisMatrix ) 
//  making bitwise OR with the last legal move of the figure ( g_pucLastNoCollision )
//
//**********************************************************************************************
void UpdateTetrixMatrix(unsigned long ulRowOffset)
{
	unsigned char ucIdx;

	for( ucIdx = 0; ucIdx < 8 ; ucIdx +=4 )
	{
		*(unsigned long *) (g_pucTetrisMatrix + 86 - ulRowOffset + ucIdx ) |=
								*(unsigned long *)(g_pucLastNoCollision + ucIdx);
								
		*(unsigned long *) (g_pucTetrisMatrix + 182 - ulRowOffset + ucIdx ) |=
								*(unsigned long *)(g_pucLastNoCollision + 8 + ucIdx ); 
	}
}

//**********************************************************************************************
//
//	Function that clear the Display Buffer 
//
//**********************************************************************************************
void ClearDisplayBuf(void)
{
	unsigned char ucIdx;

	for( ucIdx = 0; ucIdx < 20 ; ucIdx +=4 )
	{
		*(unsigned long *) (g_pucDisplayBuf + ucIdx ) = 0;
	}
}

//**********************************************************************************************
//
// function that copy the last legal move to g_pucLastNoCollision
//
//**********************************************************************************************
void CopyToLastNoCollision(void)
{
	unsigned char ucIdx;

	for( ucIdx = 0; ucIdx < 8 ; ucIdx +=4 )
	{
		*(unsigned long *) (g_pucLastNoCollision + ucIdx ) = 
						*(unsigned long *) (g_pucDisplayBuf + ucIdx );

		*(unsigned long *) (g_pucLastNoCollision + 8 + ucIdx ) = 
						*(unsigned long *) (g_pucDisplayBuf + 10 + ucIdx );

	}
}
//*********************************************************************************************
//
//	 check flags while collision occure and make desigion what to do next
//
//*********************************************************************************************
void CheckFlags(void)
{			
	unsigned char ucIdx;
	if( HWREGBITW(&g_ulFlags, FLAG_NEWFIG ) == 1 )
	{
		OSRAM96x16x1Clear();
		OSRAM96x16x1StringDraw( "GAME OVER" , 20 , 0 );
		OSRAM96x16x1StringDraw( "SCORE:" , 0 , 1 );
		for( ucIdx = 0 ; ucIdx < 10 ; ucIdx+=2 )
			*(unsigned short *)(g_pcScoreStr + ucIdx) = 0;
		sprintf(g_pcScoreStr,"%10ul", g_ulScore);
		g_ulScore = 0;
		OSRAM96x16x1StringDraw( g_pcScoreStr , 36 , 1 ); 

		HWREGBITW(&g_ulFlags, FLAG_GAMEOVER ) = 1;		
	}
	else if(HWREGBITW(&g_ulFlags, FLAG_ROT ) == 1)
	{
		ucCurrShape = ucPrevShape;
		HWREGBITW(&g_ulFlags, FLAG_ROT ) = 0;		
	}
	else if( HWREGBITW(&g_ulFlags, FLAG_MOVE ) == 1 )
	{
		g_ulADCData = ucCurrPos = ucPrevPos;
		HWREGBITW(&g_ulFlags, FLAG_MOVE ) = 0;
	}
	else
	{
		UpdateTetrixMatrix(ulRowOffset - 2);
		RowCheck();
		HWREGBITW(&g_ulFlags, FLAG_NEWFIG ) = 1	;
	}

}
//*********************************************************************************************
//
//	Function for move rows  
//
//*********************************************************************************************
void RowDel(unsigned char ucRowPos)
{
	// cikul dokato ne stignem do 0lev red toest mqsto kydeto ve4e nqma zna4e6ti redove
	while( ( ( g_pucTetrisMatrix[ucRowPos+2] | g_pucTetrisMatrix[ 96 + ucRowPos+2] ) != 0 ) | !ucRowPos )
	{	 
		 // razmenqme 2x2 redovete 0 sus 2 i 1 s 3, toest izdigame ve4e praznite redove nagore v matricata
		 g_pucTetrisMatrix[ucRowPos] ^= g_pucTetrisMatrix[ucRowPos+2] ^=
		 				g_pucTetrisMatrix[ucRowPos] ^= g_pucTetrisMatrix[ucRowPos+2];
		 g_pucTetrisMatrix[96+ucRowPos] ^= g_pucTetrisMatrix[96+ucRowPos+2] ^=
		 				g_pucTetrisMatrix[96+ucRowPos] ^= g_pucTetrisMatrix[96+ucRowPos+2];

		 g_pucTetrisMatrix[ucRowPos+1] ^= g_pucTetrisMatrix[ucRowPos+3] ^=
		 				g_pucTetrisMatrix[ucRowPos+1] ^= g_pucTetrisMatrix[ucRowPos+3];
		 g_pucTetrisMatrix[96+ucRowPos+1] ^= g_pucTetrisMatrix[96+ucRowPos+3] ^=
		 				g_pucTetrisMatrix[96+ucRowPos+1] ^= g_pucTetrisMatrix[96+ucRowPos+3];

		 ucRowPos += 2;
	}
}
//*********************************************************************************************
//
//	Function for check for full rows , make remove and redraw Tetris matrix on the display
//
//*********************************************************************************************
void RowCheck(void)
{
	unsigned char ucRowPos = 0;
	unsigned char ucRowCount = 0;
	// cikul dokato ne stignem do 0lev red toest mqsto kydeto ve4e nqma zna4e6ti redove
	while( ( ( g_pucTetrisMatrix[ucRowPos+2] | g_pucTetrisMatrix[ 96 + ucRowPos+2] ) != 0 ) | !ucRowPos )
	{
		// ako teku6tiq i sledva6tiq red sa pulni s edenici vikame funkciqta RowDel();
		if( ( ( g_pucTetrisMatrix[ucRowPos] & g_pucTetrisMatrix[ 96 + ucRowPos] ) == 0xff ) &&
					( ( g_pucTetrisMatrix[ucRowPos+1] & g_pucTetrisMatrix[ 96 + ucRowPos+1] ) == 0xff ) )	 {
			g_pucTetrisMatrix[ucRowPos] = g_pucTetrisMatrix[ 96 + ucRowPos] = 0x00;
			g_pucTetrisMatrix[ucRowPos+1] = g_pucTetrisMatrix[ 96 + ucRowPos+1] = 0x00;
			RowDel(ucRowPos);
			ucRowCount++;
		}// v protiven slu4ai minavame na sledva6tata 2ka redove
		else
			ucRowPos +=2;
	}
	//redraw-vame tetris poleto
	if(ucRowCount)
		g_ulScore += ucRowCount*10 + (ucRowCount-1)*10; 
	IntMasterDisable();
	OSRAM96x16x1ImageDraw( g_pucTetrisMatrix  , 0, 0 , 96 , 2  );
	IntMasterEnable();
			
}
//*********************************************************************************************
//
//	Function that makes the gameplay 
//
//*********************************************************************************************
void Worker(void)
{
	static unsigned long ulCount, ulBits = 0;
	
	while( !HWREGBITW(&g_ulFlags, FLAG_WORKER ) ){}
	
	HWREGBITW(&g_ulFlags, FLAG_ROT ) = HWREGBITW(&g_ulFlags, FLAG_CLICK );
	HWREGBITW(&g_ulFlags, FLAG_CLICK ) = 0;

	g_ulADCData = ( (g_ulADCData*g_pucScale[ucCurrShape])/1023) << 1;
	// proverqva dali sme v dunoto na display-a
	if( ulRowOffset > 86 ) {
		UpdateTetrixMatrix(ulRowOffset-2);
		RowCheck();
		HWREGBITW(&g_ulFlags, FLAG_NEWFIG ) = 1;				    
	}

	newFigure:
	if( ( HWREGBITW(&g_ulFlags, FLAG_GAMEOVER ) ) == 1 )
		return; 
	// proverqva flaga za nova figurkra ako e vdignat izbira formata i q zadava da e v na4aloto na display-a
	if( ( HWREGBITW(&g_ulFlags, FLAG_NEWFIG ) ) == 1 ) {
		ucCurrShape = ((TimerValueGet(TIMER0_BASE, TIMER_B )*(g_ulADCData+1)*(ucCurrShape+1)) % 19) ;
		ulRowOffset = 0;
		ucCurrPos = 4;
	} else {
		if(g_ulADCData != ucPrevPos )  // proverqva dali ima promqna v stoinostta na ADC
		{						   // i ako Da vdiga flaga za dvijenie
			ucCurrPos = g_ulADCData;
			HWREGBITW(&g_ulFlags, FLAG_MOVE ) = 1;
	
		}
		if( (HWREGBITW(&g_ulFlags, FLAG_ROT ) ) == 1)// ako imame vdignat flag za rotaciq zapisvame starata sotinost i
		{						  // izbirame sledva6tata vuzmojna rotaciq
			ucPrevShape = ucCurrShape;
			ucCurrShape = g_pucRot[ucCurrShape];
		}
	} 
	// cikul vurtq6 se po redovete na figurkata
	for( ulCount = 0 ; ulCount < 8 ; ulCount++ )
	{
		 // Ako poziciqta na koqto iskame da poztavim leviq dolen krai na figurkata e po-malka ot 8
		 // zna4i veroqtno 6te imame prenost ot ediniq red kum drugiq
		 if(ucCurrPos < 8 )
		 {
		 	// imame premestvane na dqsno sus suotvetniq broi bitove i otrqzvane na izli6nata 4ast
		 	ulBits = ( g_pucFigures[ucCurrShape][ulCount] << ucCurrPos ) & 0xff;
				// pravime XOR na teku6tiq red na figurkata sus reda ot matricata i zapisvame v
				// display buffera 
				g_pucDisplayBuf[ulCount] =  g_pucTetrisMatrix[86 - ulRowOffset + ulCount] ^ ulBits;
				//
				// proverqvame dali nqma colliziq ( 1 XOR 1 )
				if( ( g_pucDisplayBuf[ulCount] & ulBits ) != ulBits )
				{ 
					// proverka na flagovete i predpriemane na suotvetnoto deistvie
					// po zadavane na parametrite za sledva6tiq hod na figurata
					CheckFlags();
					ClearDisplayBuf();
					goto newFigure;
				}
			// otmestvaneto e tuk e nalqvo za da otrejem bitovete koito sa zapisani ve4e v 1viq red na display-a
			ulBits =  g_pucFigures[ucCurrShape][ulCount] >> ( 8 - ucCurrPos );

				// analogi4no XOR i proverka
				g_pucDisplayBuf[10 + ulCount] = g_pucTetrisMatrix[182 - ulRowOffset + ulCount] ^ ulBits;
				if( (g_pucDisplayBuf[10 + ulCount] & ulBits ) != ulBits )
				{
					CheckFlags();
					ClearDisplayBuf();
					goto newFigure;
				}	 
		 }
		 else
		 {
		 	// ako posiciqta e > 8 sledovatelno figurkata se razpolaga samo vuv vtoriq red na display-a
			// tui kato trqbva da proverim dali v 1viq nqma da zagubim 4ast ot Tetris Matrix prehvurlqme
		 	g_pucDisplayBuf[ulCount] =  g_pucTetrisMatrix[86 - ulRowOffset + ulCount];
		 	// izmestvane suotvetno nadqsno s ( poziciq - 8 ) bita
			ulBits = g_pucFigures[ucCurrShape][ulCount] << ( ucCurrPos - 8 );
			if( (ulBits & 0xff00 ) == 0 ) // ako != 0 zna4i imame colizia i 
			{							  // produljavame izpulnenieto po block shemata
				g_pucDisplayBuf[10 + ulCount] = g_pucTetrisMatrix[182 - ulRowOffset + ulCount] ^ ulBits;
				if( ( g_pucDisplayBuf[10 + ulCount] & ulBits ) != ulBits )
				{
					CheckFlags();
					ClearDisplayBuf();
					goto newFigure;						
				}
			}
			else
			{
					CheckFlags();
					ClearDisplayBuf();
					goto newFigure;
			}
		 }
	}
	// cikul za poslednite 2 reda ot display buffera za da nqma zaguba na danni ot matricata
	for( ; ulCount < 10 ; ulCount++ )
	{
		g_pucDisplayBuf[ ulCount ] ^=  g_pucTetrisMatrix[ 86 - ulRowOffset + ulCount ];
		g_pucDisplayBuf[ 10 + ulCount ] ^=  g_pucTetrisMatrix[ 182 - ulRowOffset + ulCount ]; 
	}
   	IntMasterDisable();
	OSRAM96x16x1ImageDraw( g_pucDisplayBuf  , 86 - ulRowOffset  , 0 , 10 , 2  );
	IntMasterEnable();
	ulRowOffset +=2 ;
	// nulirane na flagovete za dvijenie nova figura i rotaciq i worker
	g_ulFlags &= 0x87;
	ucPrevPos = ucCurrPos;
	ucPrevShape = ucCurrShape;
	CopyToLastNoCollision();
	ClearDisplayBuf();
}
